use crate::token::{Token, TokenKind};

fn is_symbol(c: char) -> bool {
   let symbols = ['+', '-', '*', '/', '^', '(', ')', ','];
   symbols.contains(&c)
}

fn is_keyword(identifier: &str) -> bool {
   let keywords = ["IF", "ELSE", "TRUE", "FALSE"];
   keywords.contains(&identifier.to_uppercase().as_str())
}

#[derive(Debug, Clone)]
pub struct Lexer {
   input: String,
   pos: usize,
}

impl Lexer {
   pub fn new(input: &str) -> Self {
      Self {
         input: input.to_string(),
         pos: 0,
      }
   }

   pub fn next_token(&mut self) -> Option<Token> {
      self.skip_whitespace();

      let cur_char = self.current_char();
      cur_char.map(|ch| match ch {
         c if c.is_ascii_digit() => self.consume_number(),
         c if c.is_alphabetic() => self.consume_alphabetic(),
         c if is_symbol(c) => self.consume_symbol(c),
         c => {
            eprintln!("Unexpected character: {c}");
            Token::eof()
         }
      })
   }

   fn current_char(&self) -> Option<char> {
      self.input.chars().nth(self.pos)
   }

   fn next_char(&mut self) -> Option<char> {
      let cur_char = self.current_char();
      self.pos += 1;
      cur_char
   }

   fn skip_whitespace(&mut self) {
      while let Some(c) = self.current_char() {
         if !c.is_whitespace() {
            break;
         }
         self.next_char();
      }
   }

   fn consume_number(&mut self) -> Token {
      let mut number = String::new();
      let mut has_decimal_point = false;
      let mut has_exponent = false;

      // 解析小数
      while let Some(c) = self.current_char() {
         if c.is_ascii_digit() {
            number.push(c);
            self.next_char();
         } else if c == '.' && !has_decimal_point {
            number.push(c);
            has_decimal_point = true;
            self.next_char();
         } else {
            break;
         }
      }

      // 解析科学记数法
      if let Some(c) = self.current_char() {
         if c == 'e' || c == 'E' {
            number.push(c);
            self.next_char();
            has_exponent = true;

            // 是否有 + 或 -
            if let Some(sign) = self.current_char() {
               if sign == '+' || sign == '-' {
                  number.push(sign);
                  self.next_char();
               }
            }

            // 读取指数
            while let Some(c) = self.current_char() {
               if c.is_ascii_digit() {
                  number.push(c);
                  self.next_char();
               } else {
                  break;
               }
            }
         }
      }

      if has_decimal_point || has_exponent {
         Token::new(TokenKind::NumberFloat(number.parse().unwrap()), &number)
      } else {
         Token::new(TokenKind::NumberInt(number.parse().unwrap()), &number)
      }
   }

   fn consume_symbol(&mut self, c: char) -> Token {
      self.next_char();
      // Token::new(TokenKind::Symbol(c.to_string()), &c.to_string())
      match c {
         '+' => Token::new(TokenKind::Plus, &c.to_string()),
         '-' => Token::new(TokenKind::Minus, &c.to_string()),
         '*' => Token::new(TokenKind::Asterisk, &c.to_string()),
         '/' => Token::new(TokenKind::Slash, &c.to_string()),
         '^' => Token::new(TokenKind::Caret, &c.to_string()),
         '(' => Token::new(TokenKind::LParen, &c.to_string()),
         ')' => Token::new(TokenKind::RParen, &c.to_string()),
         _ => Token::eof(),
      }
   }

   fn consume_identifier(&mut self) -> String {
      let mut identifier = String::new();
      while let Some(c) = self.current_char() {
         if c.is_alphanumeric() || c == '_' {
            identifier.push(c);
            self.next_char();
         } else {
            break;
         }
      }

      identifier
   }

   fn consume_alphabetic(&mut self) -> Token {
      let identifier = self.consume_identifier();
      if is_keyword(&identifier) {
         Token::new(TokenKind::Keyword(identifier.to_string()), &identifier)
      } else {
         Token::new(TokenKind::Identifier(identifier.to_string()), &identifier)
      }
   }
}
